﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Utilities;

namespace CP.SharePoint.SiteStyles.Layouts
{
    public partial class StyleWeb : LayoutsPageBase
    {
        #region Private Variables
        private List<SiteStyleItem> availableStyles = new List<SiteStyleItem>();
        private StyleInheritance selectedInheritance = new StyleInheritance();
        private StyleApplication selectedApplication = new StyleApplication();
        
        private enum StyleInheritance
        {
            InheritFromParent = 0,
            DoNotInHerit = 1
        }
        
        private enum StyleApplication
        {
            ThisSiteOnly = 0,
            ThisSiteAndAllSubsites = 1
        }
        #endregion

        /// <summary>
        /// Code that is executed when the page loads. Handles blocking access if the 
        /// proper permissions are not available, getting the available properties from
        /// the farm, populating the controls, setting the defaults, and loading the 
        /// necessary JavaScript for the page to function.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                // check security
                if (!SPContext.Current.Web.DoesUserHavePermissions(SPBasePermissions.ApplyThemeAndBorder))
                {
                    Response.Redirect(SPContext.Current.Web.Url + "/_layouts/accessdenied.aspx", true);
                }

                GetAvailableStylesFromFarm();

                if (!IsPostBack)
                {
                    PopulateAvailableStylesListBox();

                    SetFormFieldDefaults();
                }

                RegisterJavascript();

                applyStyleButton.Click += new EventHandler(applyStyleButton_Click);
                inheritanceRadioButtonList.SelectedIndexChanged += new EventHandler(inheritanceRadioButtonList_SelectedIndexChanged);
            }
            catch (Exception ex)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("CP.SharePoint.SiteStyles - Style Web Load", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
            }
        }

        /// <summary>
        /// Adds necessary javascript on the page to handle updating the
        /// preview image and description when a style is clicked from the
        /// listbox.
        /// </summary>
        private void RegisterJavascript()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<script type=\"text/javascript\">");
            sb.AppendFormat("var stylesListBoxId = '{0}';{1}", availableStylesListBox.ClientID, Environment.NewLine);
            sb.AppendLine("var availableStyles = new Array();");
            sb.AppendLine("availableStyles.push(new styleItem('', ''));");
            foreach (SiteStyleItem item in availableStyles)
            {
                sb.AppendFormat("availableStyles.push(new styleItem('{0}', '{1}'));{2}", item.Description, item.ThumbnailPath, Environment.NewLine);
            }
            sb.AppendLine("</script>");

            ClientScript.RegisterClientScriptBlock(this.GetType(), "SiteStyles", sb.ToString());
        }

        #region Event Handlers
        /// <summary>
        /// Method that is called when the Apply Style button is clicked and the
        /// form is submitted. Handles setting the style for the current sites and,
        /// if requested, all sub sites.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void applyStyleButton_Click(object sender, EventArgs e)
        {
            try
            {
                using (SPLongOperation operation = new SPLongOperation(this.Page))
                {
                    operation.Begin();

                    SPSite site = SPContext.Current.Site;
                    SPWeb web = SPContext.Current.Web;

                    // get the values from the currently selected settings                    
                    selectedInheritance = (StyleInheritance)Enum.Parse(typeof(StyleInheritance), inheritanceRadioButtonList.SelectedValue);
                    selectedApplication = (StyleApplication)Enum.Parse(typeof(StyleApplication), applyToRadioButtonList.SelectedValue);

                    if (selectedInheritance == StyleInheritance.DoNotInHerit)
                    {
                        int styleId = int.Parse(availableStylesListBox.SelectedItem.Value);

                        if (styleId > 0)
                        {
                            // find the style from the list using the id
                            SiteStyleItem style = availableStyles.Find(delegate(SiteStyleItem i) { return i.Id == styleId; });

                            // turn on site styling
                            EnableSiteStyle(web, style);

                            if (selectedApplication == StyleApplication.ThisSiteAndAllSubsites)
                            {
                                EnableSiteStyleForSubWebs(web, style);
                            }
                        }
                        else
                        {
                            // turn off site styling
                            DisableSiteStyle(web);

                            if (selectedApplication == StyleApplication.ThisSiteAndAllSubsites)
                            {
                                DisableSiteStyleForSubWebs(web);
                            }
                        }
                    }
                    else
                    {                        
                        using (SPWeb parentWeb = web.ParentWeb)
                        {
                            if (parentWeb.Properties.ContainsKey(Resources.PropertyCurrentSiteStyle))
                            {
                                int styleId = int.Parse(parentWeb.Properties[Resources.PropertyCurrentSiteStyle].ToString());

                                SiteStyleItem style = availableStyles.Find(delegate(SiteStyleItem i) { return i.Id == styleId; });
                                EnableSiteStyle(web, style);
                            }
                        }
                    }

                    operation.End(
                        SPContext.Current.Web.Url + "/_layouts/settings.aspx",
                        SPRedirectFlags.DoNotEncodeUrl,
                        HttpContext.Current,
                        null);
                }
            }
            catch (Exception ex)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("CP.SharePoint.SiteStyles - Apply Style", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
            }
        }

        /// <summary>
        /// Method that is called when the inheritance radio button list
        /// has been changed. If inherit from parent is selected the styles
        /// list box is locked for editing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void inheritanceRadioButtonList_SelectedIndexChanged(object sender, EventArgs e)
        {
            LockStylesListBox();
        }

        #endregion

        #region Data Population / Retreival
        /// <summary>
        ///  Gets all available styles from the SPFarm properties bag. Reads the
        ///  styles from an XML block into a data table which then populates
        ///  a List of SiteStyleItems for usage during loading and postback.
        /// </summary>
        private void GetAvailableStylesFromFarm()
        {
            SPFarm farm = SPFarm.Local;
            if (farm.Properties.ContainsKey(Resources.PropertyAvailableSiteStyles))
            {
                DataTable dt = new DataTable();
                DataSet ds = new DataSet();                    
                StringReader reader = new StringReader(farm.Properties[Resources.PropertyAvailableSiteStyles].ToString());
                ds.ReadXml(reader);
                dt = ds.Tables[0];
                    
                if (dt != null)
                {
                    foreach (DataRow row in dt.Rows)
                    {
                        SiteStyleItem styleItem = new SiteStyleItem();

                        styleItem.Id = int.Parse(row["ID"].ToString());
                        styleItem.Title = row["Title"].ToString();
                            
                        if (dt.Columns.Contains(Resources.FieldNameDescription))
                            styleItem.Description = row[Resources.FieldNameDescription].ToString();
                        else
                            styleItem.Description = string.Empty;

                        if (dt.Columns.Contains(Resources.FieldNameCssPath))
                            styleItem.CssPath = row[Resources.FieldNameCssPath].ToString();
                        else
                            styleItem.CssPath = string.Empty;

                        if (dt.Columns.Contains(Resources.FieldNameThumbnail))
                            styleItem.ThumbnailPath = row[Resources.FieldNameThumbnail].ToString();
                        else
                            styleItem.ThumbnailPath = string.Empty;

                        if (dt.Columns.Contains(Resources.FieldNameThemable))
                            styleItem.Themable = ParseBool(row[Resources.FieldNameThemable].ToString());
                        else
                            styleItem.Themable = false;

                        availableStyles.Add(styleItem);
                    }
                }
            }
        }

        /// <summary>
        /// Populates the items in the styles list box based on the information
        /// retrieved from the SPFarm properties bag in the GetAvailableStylesFromFarm
        /// method.
        /// </summary>
        private void PopulateAvailableStylesListBox()
        {
            availableStylesListBox.Items.Clear();

            availableStylesListBox.DataTextField = "Title";
            availableStylesListBox.DataValueField = "ID";
            availableStylesListBox.DataSource = availableStyles;
            availableStylesListBox.DataBind();

            availableStylesListBox.Items.Insert(0, new ListItem("Default (no style)", "-1"));            
        }

        /// <summary>
        /// Will lock the styles list box to prevent editing depending on the 
        /// current state of the inheritance radio button list.
        /// </summary>
        private void LockStylesListBox()
        {
            selectedInheritance = (StyleInheritance)Enum.Parse(typeof(StyleInheritance), inheritanceRadioButtonList.SelectedValue);

            if (selectedInheritance == StyleInheritance.InheritFromParent)
            {
                availableStylesListBox.Enabled = false;
            }
            else
            {
                availableStylesListBox.Enabled = true;
            }
        }

        /// <summary>
        /// Populates the form with all defaults based on the current settings for
        /// the web.
        /// </summary>
        private void SetFormFieldDefaults()
        {
            bool itemWasSelected = false;

            SPWeb currentWeb = SPContext.Current.Web;

            // inheritance radio button list
            if (currentWeb.IsRootWeb)
            {
                inheritanceRadioButtonList.SelectedIndex = 1; // do not inherit
                inheritanceRadioButtonList.Enabled = false;                
            }
            else
            {
                if (currentWeb.Properties.ContainsKey(Resources.PropertyCurrentSiteStyleInheritance))
                {
                    inheritanceRadioButtonList.SelectedIndex = int.Parse(currentWeb.Properties[Resources.PropertyCurrentSiteStyleInheritance].ToString());
                }                

                LockStylesListBox();
            }

            // style list
            if (currentWeb.Properties.ContainsKey(Resources.PropertyCurrentSiteStyle))
            {
                foreach (ListItem li in availableStylesListBox.Items)
                {
                    if (li.Value == currentWeb.Properties[Resources.PropertyCurrentSiteStyle].ToString())
                    {
                        li.Selected = true;
                        itemWasSelected = true;
                        break;
                    }
                }
            }

            if (!itemWasSelected)
                availableStylesListBox.SelectedIndex = 0;
        }
        #endregion         

        #region Enable / Disable Site Styles
        /// <summary>
        /// Enables the site style for the specified web by setting properties in the
        /// specified web's property bag.
        /// </summary>
        /// <param name="web">The web where the style properties should be set.</param>
        /// <param name="style">The style object that contains all the necessary
        /// information regarding the currently selected style that will be set.</param>
        private void EnableSiteStyle(SPWeb web, SiteStyleItem style)
        {
            // set site style properties
            SetProperty(web, Resources.PropertyCurrentSiteStyle, style.Id.ToString());
            SetProperty(web, Resources.PropertyCurrentSiteStyleCssPath, style.CssPath);
            SetProperty(web, Resources.PropertyCurrentSiteStyleTheming, style.Themable.ToString());
            SetProperty(web, Resources.PropertyCurrentSiteStyleInheritance, inheritanceRadioButtonList.SelectedIndex.ToString());
        }

        /// <summary>
        /// Disables the site style for the specified web by removing properties in the
        /// specified web's property bag.
        /// </summary>
        /// <param name="web">The web where the style properties should be removed.</param>
        private void DisableSiteStyle(SPWeb web)
        {
            // remove the site style properties
            SetProperty(web, Resources.PropertyCurrentSiteStyle, null);
            SetProperty(web, Resources.PropertyCurrentSiteStyleTheming, null);
            SetProperty(web, Resources.PropertyCurrentSiteStyleCssPath, null);
            SetProperty(web, Resources.PropertyCurrentSiteStyleInheritance, null);
        }

        /// <summary>
        /// Loops through all subwebs for the specified web and sets the style
        /// properties based on the style parameter.
        /// </summary>
        /// <param name="web">The base web for which all sub webs will be processed.</param>
        /// <param name="style">The style that will be applied to all sub webs.</param>
        private void EnableSiteStyleForSubWebs(SPWeb web, SiteStyleItem style)
        {
            foreach (SPWeb subWeb in web.Webs)
            {
                if (subWeb.Webs.Count > 0)
                {
                    EnableSiteStyleForSubWebs(subWeb, style);
                }

                EnableSiteStyle(subWeb, style);

                subWeb.Dispose();
            }
        }

        /// <summary>
        /// Loops through all subwebs for the specified web and removes the site
        /// style.
        /// </summary>
        /// <param name="web">The base web for which all sub webs will be processed.</param>
        private void DisableSiteStyleForSubWebs(SPWeb web)
        {
            foreach (SPWeb subWeb in web.Webs)
            {
                if (subWeb.Webs.Count > 0)
                {
                    DisableSiteStyleForSubWebs(subWeb);
                }

                DisableSiteStyle(subWeb);

                subWeb.Dispose();
            }
        }
        #endregion

        #region Utility
        /// <summary>
        /// Parses a boolean from a string. Includes special handling of "0" and "1" due
        /// to the fact that bool.TryParse does not support "0" or "1".
        /// </summary>
        /// <param name="boolString">A string version of a boolean.</param>
        /// <returns>True or False based on the value of the string.</returns>
        private bool ParseBool(string boolString)
        {
            bool result;
            if (bool.TryParse(boolString, out result))
                return result;

            try
            {
                int i = Convert.ToInt32(boolString);
                return Convert.ToBoolean(i);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Sets a property on a web based on the provided parameters and then
        /// saves the changes to the web properties bag.
        /// </summary>
        /// <param name="web">The web that contains the properties bag to be set/updated.</param>
        /// <param name="key">The property name that should be set/updated.</param>
        /// <param name="value">The property value that should be used for the given
        /// property.</param>
        private void SetProperty(SPWeb web, string key, string value)
        {
            if (web.Properties.ContainsKey(key))
                web.Properties[key] = value;
            else
                web.Properties.Add(key, value);
            
            web.Properties.Update();
        }
        #endregion
    }
}
